#include "qe_def.h"
#include "qe_log.h"
#include "qe_assert.h"
#include "qe_string.h"
#include "qe_debug.h"



QELOG_DOMAIN(QELOG_DOMAIN_DBGCTRL);



QE_LIST_INIT(dbgctrl_list);



qe_list *qe_dbgctrl_get_list(void)
{
    return &dbgctrl_list;
}

qe_ret qe_dbgctrl_init(qe_dbgctrl *ctrl, qe_const_str name,
    qe_u32 rate, qe_u32 once, qe_s32 max,
    qe_dbgctrl_callback cb, qe_ptr user)
{
    if (!ctrl || !name || !cb) {
        qe_error("pararms invalid");
        return qe_err_param;
    }

    ctrl->en       = qe_false;
    ctrl->running  = qe_false;
    ctrl->name     = qe_strdup(name);
    ctrl->rate     = rate;
    ctrl->once     = once;
    ctrl->max      = max;
    ctrl->user     = user;
    ctrl->callback = cb;

    qe_dbgctrl_clear(ctrl);
    
    qe_debug("ctrl %s rate:%d once:%d max:%d init success",
        name, rate, once, max);
    
    return qe_ok;
}

qe_dbgctrl *qe_dbgctrl_find(qe_const_str name)
{
    qe_dbgctrl *p;

    qe_list_foreach_entry(p, &dbgctrl_list, list) {
        if (qe_strcmp(p->name, name) == 0) {
            return p;
        }
    }

    return QE_NULL;
}

qe_ret qe_dbgctrl_register(qe_dbgctrl *ctrl)
{
    qe_assert(ctrl != QE_NULL);

    if (qe_dbgctrl_find(ctrl->name) != QE_NULL) {
        qe_warning("ctrl %s exist", ctrl->name);
        return qe_err_exist;
    }

    qe_list_append(&ctrl->list, &dbgctrl_list);
    qe_debug("ctrl %s register", ctrl->name);
    return qe_ok;
}

void qe_dbgctrl_unregister(qe_dbgctrl *ctrl)
{
    qe_assert(ctrl != QE_NULL);
    qe_list_remove(&ctrl->list);
    qe_debug("ctrl %s unregister", ctrl->name);
}

qe_u32 qe_dbgctrl_get_bps(qe_dbgctrl *ctrl)
{
    return ctrl->bps;
}

qe_u32 qe_dbgctrl_get_pps(qe_dbgctrl *ctrl)
{
    return ctrl->pps;
}

qe_u32 qe_dbgctrl_get_loss(qe_dbgctrl *ctrl)
{
    return ctrl->loss;
}

qe_u32 qe_dbgctrl_get_count(qe_dbgctrl *ctrl)
{
    return ctrl->count;
}

qe_u32 qe_dbgctrl_get_rate(qe_dbgctrl *ctrl)
{
    return ctrl->rate;
}

void qe_dbgctrl_set_rate(qe_dbgctrl *ctrl, qe_u32 rate)
{
    ctrl->rate = rate;
    qe_debug("ctrl %s set rate:%d", ctrl->name, rate);
}

void qe_dbgctrl_set_max(qe_dbgctrl *ctrl, qe_s32 max)
{
    ctrl->max = max;
    qe_debug("ctrl %s set max:%d", ctrl->name, max);
}

void qe_dbgctrl_set_once(qe_dbgctrl *ctrl, qe_u32 once)
{
    ctrl->once = once;
    qe_debug("ctrl %s set once:%d", ctrl->name, once);
}

void qe_dbgctrl_enable(qe_dbgctrl *ctrl)
{
    ctrl->en = qe_true;
    qe_debug("ctrl %s enable", ctrl->name);
}

void qe_dbgctrl_disable(qe_dbgctrl *ctrl)
{
    ctrl->en = qe_false;
    qe_debug("ctrl %s disable", ctrl->name);
}

static void invoke_callback(qe_dbgctrl *ctrl)
{
    int i;

    for (i=0; i<ctrl->once; i++) {
        ctrl->callback(ctrl, ctrl->user);
    }
}

void qe_dbgctrl_invoke_callback(qe_dbgctrl *ctrl)
{
    if (!ctrl->en)
        return;

    ctrl->count++;

    invoke_callback(ctrl);

    if (ctrl->max > 0 && ctrl->count >= ctrl->max) {
        qe_dbgctrl_stop(ctrl);
    }
}

void qe_dbgctrl_count_increase(qe_dbgctrl *ctrl)
{
    if (!ctrl->en)
        return;
    
    ctrl->count++;

    if (!(ctrl->count % ctrl->rate)) {
        
        invoke_callback(ctrl);
        
        if (ctrl->max > 0 && ctrl->count >= ctrl->max) {
            qe_dbgctrl_stop(ctrl);
        }
    }
}

void qe_dbgctrl_tps_update(qe_dbgctrl *ctrl, qe_time_t pts, qe_u32 bytes)
{
    double bps;
    double pps;

    if (!ctrl->en)
        return;

    ctrl->bps_bytes += bytes;
    ctrl->count++;
    ctrl->pps_count++;

    if (!ctrl->pts) {
        ctrl->pts = pts;
        return;
    }

    if ((pts - ctrl->pts) >= ctrl->rate) {
        ctrl->pts = pts;
        bps = ctrl->bps_bytes * 8.0 * 1000.0 / ctrl->rate;
        pps = ctrl->pps_count * 1000.0 / ctrl->rate;
        ctrl->bps = (qe_u32)bps;
        ctrl->pps = (qe_u32)pps;
        invoke_callback(ctrl);
        ctrl->bps_bytes = 0;
        ctrl->pps_count = 0;
    }
}

void qe_dbgctrl_count_update(qe_dbgctrl *ctrl, qe_u32 count)
{
    if (!ctrl->en)
        return;

    if (ctrl->count && count != (ctrl->count+ctrl->once)) {
        ctrl->loss += count - ctrl->count - ctrl->once;
    }

    ctrl->count = count;

    if (!(count % ctrl->rate)) {

        invoke_callback(ctrl);

        ctrl->loss = 0;

        if (ctrl->max > 0 && count >= ctrl->max) {
            qe_dbgctrl_stop(ctrl);
        }
    }
}

void qe_dbgctrl_start(qe_dbgctrl *ctrl)
{
    qe_dbgctrl_clear(ctrl);
    qe_dbgctrl_enable(ctrl);
    ctrl->running = qe_true;
    qe_debug("ctrl %s start", ctrl->name);
}

void qe_dbgctrl_stop(qe_dbgctrl *ctrl)
{
    ctrl->running = qe_false;
    qe_dbgctrl_disable(ctrl);
    qe_dbgctrl_clear(ctrl);
    qe_debug("ctrl %s stop", ctrl->name);
}

qe_bool qe_dbgctrl_should_stop(qe_dbgctrl *ctrl)
{
    return (!ctrl->running);
}

qe_bool qe_dbgctrl_is_enable(qe_dbgctrl *ctrl)
{
    return (ctrl->en);
}

void qe_dbgctrl_clear(qe_dbgctrl *ctrl)
{
    ctrl->count     = 0;
    ctrl->pts       = 0;
    ctrl->loss      = 0;
    ctrl->bps       = 0;
    ctrl->bps_bytes = 0;
    ctrl->pps       = 0;
    ctrl->pps_count = 0;
}